#pragma once

#include "Mutex.h"
#include <iostream>
#include <string>
#include <cstring>
#include <ctime>
#include <filesystem>
#include <sys/types.h>
#include <unistd.h>
#include <sstream>
#include <memory> // 暂时先不用

namespace zmh
{
    class bash_refresh
    {
    public:
        virtual void refresh(const std::string &mes) = 0;

    protected:
        Mutex _mutex; // 保证刷新策略原子性的
    };

    class stdout_refresh : public bash_refresh
    {
    public:
        virtual void refresh(const std::string &mes) override
        {
            auto_Mutex guard(_mutex);
            std::cout << mes << std::endl;
        }
    };

    class file_refresh : public bash_refresh
    {
    public:
        file_refresh(const std::string &file_path, const std::string &file_name)
            : _file_path(file_path), _file_name(file_name)
        {
            if (std::filesystem::exists(file_path) == true)
                return;
            else
                std::filesystem::create_directories(_file_path); // 目前暂未对异常进行try catch
        }
        virtual void refresh(const std::string &mes) override
        {
            const std::string &path_name = _file_path + (_file_path.back() != '/' ? "/" : "") + _file_name;
            {
                auto_Mutex guard(_mutex);
                auto pf = fopen(path_name.c_str(), "a"); // 追加的形式打开或新建
                if (pf == nullptr)
                    throw "打开文件出现错误" + std::string(strerror(errno));
                fprintf(pf, "%s\r\n", mes.c_str());
                fclose(pf);
            }
        }

    private:
        std::string _file_path;
        std::string _file_name;
    };

    enum LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string LogLevelTable[] = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"}; // 转移表

    class Log;

    class LogMessage // 主要负责生成一条日志消息
    {
        std::string GetTime() const
        {
            time_t time_mess = time(nullptr);
            struct tm time_format_mess = {0};
            if (!localtime_r(&time_mess, &time_format_mess))
                throw "无效时间";
            char time_buffer[20] = {0};
            snprintf(time_buffer, sizeof(time_buffer), "%04d-%02d-%02d-%02d:%02d:%02d",
                     time_format_mess.tm_year + 1900,
                     time_format_mess.tm_mon + 1,
                     time_format_mess.tm_mday,
                     time_format_mess.tm_hour,
                     time_format_mess.tm_min,
                     time_format_mess.tm_sec);
            return time_buffer;
        }

    public:
        LogMessage(const LogLevel level, const std::string &filename, const std::string &line, bash_refresh *ptr)
            : _ptr(ptr)
        {
            // 采用C++字符串的拼接
            _format += "[" + GetTime() + "] ";
            _format += "[" + LogLevelTable[level] + "] ";
            _format += "[" + std::to_string(getpid()) + "] ";
            _format += "[" + filename + "] ";
            _format += "[" + line + "] ";
        }
        ~LogMessage()
        {
            // 执行刷新策略
            _ptr->refresh(_format + _oss.str());
        }
        template <class T>
        LogMessage &operator<<(T &&mes)
        {
            _oss << mes;
            return *this;
        }

    private:
        std::string _format;
        std::ostringstream _oss;
        bash_refresh *_ptr;
    };

#define DEFAULT_PATH "./log/"
#define DEFAULT_NAME "zmh.log"

    class Log // 行为：1. 创建LogMessage类 2. 为LogMessage类提供刷新策略
    {
        friend class LogMessage; // LogMessage可使用Log的所有方法与成员

    public:
        Log(bool strategy)
        {
            if (strategy == true)
                ptr = new stdout_refresh;
            else
                ptr = new file_refresh(DEFAULT_PATH, DEFAULT_NAME);
        }
        Log(bool strategy, const std::string &file_path, const std::string &file_name)
        {
            if (strategy == true)
                ptr = new stdout_refresh;
            else
                ptr = new file_refresh(file_path, file_name);
        }
        ~Log()
        {
            delete ptr;
        }
        void set(bool strategy)
        {
            bash_refresh *tmp = nullptr;

            if (strategy == true)
                tmp = new stdout_refresh;
            else
                tmp = new file_refresh(DEFAULT_PATH, DEFAULT_NAME);

            if (tmp == nullptr)
                std::cerr << "void Log::set(bool) mistake" << std::endl;
            else
                ptr = tmp;
        }
        void set(bool strategy, const std::string &file_path, const std::string &file_name)
        {
            bash_refresh *tmp = nullptr;

            if (strategy == true)
                tmp = new stdout_refresh;
            else
                tmp = new file_refresh(file_path, file_name);

            if (tmp == nullptr)
                std::cerr << "void Log::set(bool, const std::string, const std::string) mistake" << std::endl;
            else
                ptr = tmp;
        }

        template <class T1, class T2>
        LogMessage operator()(const LogLevel level, T1 &&filename, T2 &&line)
        {
            std::ostringstream oss; // 使用流,可以处理任意格式
            oss << filename;
            std::string s_filename = oss.str();
            oss.str(""); // 清空字符串
            oss << line;
            std::string s_line = oss.str();
            return LogMessage(level, s_filename, s_line, this->ptr);
        }

    private:
        bash_refresh *ptr; // 基类指针,目的为了实现多态
        bool _strategy;    // 日志刷新策略 --- true标准输出刷新 false文件刷新
    };

    Log log(true); // 全局的变量
    #define LOG(x) zmh::log(x, __FILE__, __LINE__)
    #define SET_LOG(flag) zmh::log.set(flag) // true 标准输出 false标准输入
}